﻿
param
(
    #FQDN to SCCM Server
    [Parameter(Mandatory=$true)][string]$SiteServer, 
    [Parameter(Mandatory=$true)][string]$xmlConfigFile
)

import-module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + "\ConfigurationManager.psd1")
[System.Reflection.Assembly]::LoadWithPartialName("System.web")

[xml]$global:doc = (New-Object System.Net.WebClient).DownloadString("http://$SiteServer/$xmlConfigFile")


#Using these functions inside these PS1 files
. "$PSScriptRoot\keepass.ps1"
. "$PSScriptRoot\MaintenanceWindows.ps1"
. "$PSScriptRoot\sqlreporting.ps1"
. "$PSScriptRoot\CA-ServiceDesk.ps1" -SiteServer $SiteServer -xmlConfigFile $xmlConfigFile


Add-Type -TypeDefinition @"
    //What day to return
    public enum DayToReturnInMonth
    {
        FirstMondayOfTheMonth,
        FirstThursdayOfTheMonth,
        SecondMondayOfTheMonth,
        SecondTuesdayOfTheMonth,
        SecondWednesdayOfTheMonth,
        SecondThursdayOfTheMonth,
        ForthMondayOfTheMonth,
        ThirdSundayOfTheMonth,
        ForthSundayOfTheMonth,
        ThirdMondayOfTheMonth,
        ThirdTuesdayOfTheMonth,
        ThirdWednesdayOfTheMonth,
        ThirdThursdayOfTheMonth,
        ThirdFridayOfTheMonth,
        ThirdSundayOfTheMonthPlus1,
        FirstMondayOfTheMonthPlus1,
        SecondMondayOfTheMonthPlus1,
        MonthYearPlus1
    }
"@


$global:PrestageDrive= $global:doc.ADRs.Common.PrestageDrive
$global:PreStagePackage = $global:doc.ADRs.Common.PreStagePackage
$global:PrimaryDP = $global:doc.ADRs.Common.PrimaryDP
$global:SUPPackage = (get-date -Format $global:doc.ADRs.Common.SUPPackageDateFormat)+$global:doc.ADRs.Common.SUPPackagePrefix
$global:UpdateClassification = $global:doc.ADRs.Common.UpdateClassification
$global:DateReleasedOrRevised = [Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DateReleasedOrRevisedType]::($global:doc.ADRs.Common.DateReleasedOrRevised)
$global:Title = $global:doc.ADRs.Common.Title
$global:ADRName = $global:doc.ADRs.Common.ADRName
$global:waiting = $global:doc.ADRs.Common.waiting
$global:SourceServerUNC = $global:doc.ADRs.Common.SourceServerUNC
$global:DPGroup = $global:doc.ADRs.Common.DPGroup
$global:PSDrive = $global:doc.ADRs.Common.PSDrive +":\"
$global:PackageID
$global:retryWaiting = [int]$global:doc.ADRs.Common.retryWaiting

$global:AllSUG = ""

$Summary += Get-Date -Format F
[array]$global:attrVal = @()



Function Main
{

        #Create folder on share if missing
        New-FolderOnShare

        #Create new "Deployment Package"
        New-CMSUPPackage -SiteCode $global:doc.ADRs.Common.PSDrive -Name $global:SUPPackage -Source $global:SourceServerUNC -SiteServer $SiteServer

        #cd $global:PSDrive

        Run-ADR -ADRName

        Rename-SUG

        Distribute-To-Primary-DP -SiteServer $SiteServer

        #TODO Change order parameter insteed of getting it inside for better flexibility
        Create-SoftwareUpdateDeployments -FromWhatDateTime (Get-Date) -PSDrive $global:doc.ADRs.Common.PSDrive -xmlConfiguration $global:doc -SoftwareUpdateGroup $global:SUPPackage

        #Todo better parameters that can be given to function
        if ((Find-SUPPackageIfInstalled -PackageID $global:AllSUG.PackageID -SiteServer $SiteServer) -eq $true)
        {
            #Todo better parameters that can be given to function
            if (Prestage-SoftwareUpdate-Package -eq $true)
            {
                #Todo better parameters that can be given to function
                Distribute-To-DPGroup
            }
        }

}


Function Main-Version2
{

    #$Titles = @()

    $SingleSUPPackages = $Global:doc.ADRs.Common.SingleSUPPackages.SingleSUPPackage
    foreach($SingleSUPPackage in $SingleSUPPackages)
    {
        $SingleSUPPackageName = $SingleSUPPackage.'#text'.Trim()
        #add the later exclusion parameters when creating the big SUD
        #$Titles += $SingleSUPPackageName #$Titles += ("-"+$SingleSUPPackageName)
        Main-Sub -SingleSUPPackageName (" - "+$SingleSUPPackageName.Replace(",","")) -SiteServer $SiteServer -Product $SingleSUPPackageName -DeleteSUGWhenFound $true -ADRParameters $global:Title -ProductSearch $true -DPGroup $SingleSUPPackage.DPGroup
    }


    #Download all patches again and delete the package/SUP without deleting the software update deployment (thuse creating a SUD with all patches that can use ALL the newly created SUP)
    Main-Sub -SiteServer $SiteServer -DeleteSUGWhenFound $false -ADRParameters $Global:doc.ADRs.Common.Title -ProductSearch $false

    $SUPPackage = (get-date -Format $global:doc.ADRs.Common.SUPPackageDateFormat)+$global:doc.ADRs.Common.SUPPackagePrefix

    $xmlConfiguration = $global:doc
    $ChangeOrderNumber = Get-CAServiceDeskChangeOrder -XMLChangeFileSaveLocation $xmlConfiguration.ADRs.Common.XMLChangeFileSaveLocation -YearMonthToGet (get-date -Format $global:doc.ADRs.Common.SUPPackageDateFormat)

    Create-SoftwareUpdateDeployments -FromWhatDateTime (Get-Date) -PSDrive $global:doc.ADRs.Common.PSDrive -xmlConfiguration $xmlConfiguration -SoftwareUpdateGroup $SUPPackage -ChangeOrderNumber $ChangeOrderNumber

}

Function Main-Sub
{
param
(
    $SingleSUPPackageName,
    $SiteServer,
    $Product,
    $DeleteSUGWhenFound,
    $ADRParameters,
    [bool]$ProductSearch,
    $DPGroup
)

        $SUPPackage = (get-date -Format $global:doc.ADRs.Common.SUPPackageDateFormat)+$global:doc.ADRs.Common.SUPPackagePrefix+$SingleSUPPackageName
        #Create folder on share if missing
        New-FolderOnShare -SUPPackage $SUPPackage -SourceServerUNC $global:SourceServerUNC

        $SUPPackageFullPath = $global:SourceServerUNC+$SUPPackage

        #Create new "Deployment Package"
        New-CMSUPPackage -SiteCode $global:doc.ADRs.Common.PSDrive -Name $SUPPackage -Source $SUPPackageFullPath -SiteServer $SiteServer
        #todo $SingleSUPPackage needs changing
        Run-ADR -SUPPackage $SUPPackage -SUPPackageFullPath $SUPPackageFullPath -Product $Product -Titles $ADRParameters -ProductSearch $ProductSearch

        $FoundSUG = Rename-SUG -waiting $global:waiting -retryWaiting 1200 -DeleteSUGWhenFound $DeleteSUGWhenFound -SUPPackage $SUPPackage

        if($FoundSUG -eq $true -and $DPGroup -ne $null)
        {#package can be distributed
            Distribute-To-DPGroup -DPs $DPGroup -SUPPackage $SUPPackage
        }
        else
        {#delete package and folder on share
            Set-Location c:
            Remove-Item -Path $SUPPackageFullPath -Recurse:$true -Force:$true
            cd $global:PSDrive
            Remove-CMSoftwareUpdateDeploymentPackage -Name $SUPPackage -Force:$true
        }

}


Function New-CMSUPPackage
{
    PARAM(
         $SiteCode,
         $Name,
         $Source,
         [Parameter(Mandatory=$true)]$SiteServer
         )

    $Arguments = @{Name = $Name;Description = "";PkgSourceFlag = 2;PkgSourcePath = $Source; PkgFlags = 16777216}

    if((Get-WmiObject -Namespace "Root\SMS\Site_$SiteCode" -Class SMS_SoftwareUpdatesPackage  -ComputerName $SiteServer | where Name -eq $Name) -eq $null)
    {
        Try{
            Set-WmiInstance -Namespace "Root\SMS\Site_$SiteCode" -Class SMS_SoftwareUpdatesPackage -Arguments $Arguments -ComputerName $SiteServer -ErrorAction STOP
        }
        Catch{
            $E = $_.Exception.GetBaseException()
            $E.ErrorInformation.Description
        }
    }
}

Function Find-SUPPackageNotDistributed
{
    PARAM(
         $SiteCode,
         $PackageID,
         #Attach to an existing change order
         [Parameter(Mandatory=$true)][String]$ChangeOrder,
         [Parameter(Mandatory=$true)]$SiteServer
         )

$failures = $null

    # Find all content with State = 1, State = 2 or State = 3, see http://msdn.microsoft.com/en-us/library/cc143014.aspx


    $Arguments = @{Name = $Name;Description = "";PkgSourceFlag = 2;PkgSourcePath = $Source}

    Try{
            cd $SiteCode":"
            $temp = Get-CMDistributionPoint

            #Set-WmiInstance -Namespace "Root\SMS\Site_$SiteCode" -Class SMS_SoftwareUpdatesPackage -Arguments $Arguments -ComputerName $SiteServer -ErrorAction STOP
            $Namespace = "Root\SMS\Site_" + $SiteCode
            $failures = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE (State = 1 or State = 2 Or state = 3 Or state = 7) AND PackageID='$($PackageID)'"

            if ($failures -eq $null)
                {
    	            "No failed content distribution."
                }
            else
                {
                    foreach ($failure in $failures)
                        {
                            # Get the distribution points that content wouldn't distribute to
		                    $DistributionPoints = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer  -Query "SELECT * FROM SMS_DistributionPoint WHERE SiteCode='$($failure.SiteCode)' AND PackageID='$($failure.PackageID)'"
                            foreach ($DistributionPoint in $DistributionPoints)
                                {
                                    #check if we are really looking at the correct Distribution Point
            			            if ($DistributionPoint.ServerNALPath -eq $failure.ServerNALPath)
                                    {
                                        $failure.PackageID
                                        $ff= $failure.ServerNALPath
                                        $servername = [regex]::match("$ff”,'\w*-\w*.\w*.\w*.\w*').value
                                        
                                            for($a=0; $a -lt $temp.Count; $a++)
                                            {
                                                if ($temp[$a].NALPath -eq $DistributionPoint.ServerNALPath )
                                                {
                                                    $SCCMDPDescription = ($temp[$a].Props | where {$_.PropertyName -eq "Description"}).Value1
                                                    if($SCCMDPDescription -eq "x" -or $SCCMDPDescription -eq $null -or $SCCMDPDescription -eq "")
                                                    {
                                                        $SCCMDPDescription = "Data Centres"
                                                    }
                                                    break                                                    
                                                }
                                            }

                                        Create-CAServiceDeskRequestForUSBCopyOnDP -SCCMDP $servername -SCCMDPDescription $SCCMDPDescription -ChangeOrder $ChangeOrder
                                        
                                            # Use the RefreshNow Property to "Refresh Distribution Point", see http://msdn.microsoft.com/en-us/library/hh949735.aspx
			                	            #$DistributionPoint.RefreshNow = $true
                                            #$DistributionPoint.put()
                               
                                    }
                    
                                }
                        }
                }                    

    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}

Function Redestribute-SUPPackagePrestageMode
{
    PARAM(
         $SiteCode,
         $PackageID,
         [Parameter(Mandatory=$true)]$SiteServer
         )


    # Find all content with State = 1, State = 2 or State = 3, see http://msdn.microsoft.com/en-us/library/cc143014.aspx
    $failures = $null

    Try{
            cd $SiteCode":"

            $Namespace = "Root\SMS\Site_" + $SiteCode
            $failures = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE (State = 1 or State = 2 Or state = 3 Or state = 7) AND PackageID='$($PackageID)'"
            #$failures = Get-WmiObject -Namespace root\sms\site_$SiteCode -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE State = 1 or State = 2 Or state = 3 Or state = 7"

            $TotalNumber = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE (State = 1 or State = 2 Or state = 3 Or state = 7)"
            $TotalNumber.Count

            if ($failures -eq $null)
                {
    	            "No failed content distribution."
                }
            else
                {
                    foreach ($failure in $failures)
                        {
                            # Get the distribution points that content wouldn't distribute to
		                    #$DistributionPoints = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer  -Query "SELECT * FROM SMS_DistributionPoint WHERE SiteCode='$($SiteCode)' AND PackageID='$($failure.PackageID)'"
                    		$DistributionPoints = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer  -Query "SELECT * FROM SMS_DistributionPoint WHERE SiteCode='$($failure.SiteCode)' AND PackageID='$($failure.PackageID)'"

                            foreach ($DistributionPoint in $DistributionPoints)
                                {
                                    #check if we are really looking at the correct Distribution Point
            			            if ($DistributionPoint.ServerNALPath -eq $failure.ServerNALPath)
                                    {
                                        $failure.PackageID
                                        $DistributionPoint.ServerNALPath
                                        
                                            # Use the RefreshNow Property to "Refresh Distribution Point", see http://msdn.microsoft.com/en-us/library/hh949735.aspx
			                	            $DistributionPoint.RefreshNow = $true
                                            $DistributionPoint.put()
                               
                                    }
                    
                                }
                        }
                }                    

    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}

Function Find-SUPPackageIfInstalled
{
    PARAM(         
         $PackageID,
         [Parameter(Mandatory=$true)]$SiteServer
         )


    # Find all content with State = 1, State = 2 or State = 3, see http://msdn.microsoft.com/en-us/library/cc143014.aspx
    $failures = $null

    Try{
            cd $global:PSDrive
            $FoundSUG = $False
            $Namespace = "Root\SMS\Site_" + $global:doc.ADRs.Common.PSDrive
            $retryTimes = 0

            while ($FoundSUG -eq $False)
            {
                $failures = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE (State = 0) AND PackageID='$($PackageID)'"
                if($failures.count -eq 0)
                {
                    Write-Host "waiting $global:waiting seconds for SUP $PackageID"
                    Wait-Event -Timeout $global:waiting

                    $retryTimes +=1
                    if($retryTimes -ge $global:retryWaiting)
                    {
                        return $FoundSUG
                    }
                }
                else
                {
                    Write-Host "SUP found."
                    $FoundSUG = $true
                }

            }
            return $FoundSUG

    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}


Function Get-WeekDayInMonth ([int]$Month, [int]$year, [DayToReturnInMonth]$Day, [bool]$Plus1Month)
{
[int]$WeekDay = 0

if($Plus1Month -eq $true) 
{
    if($Month -eq 12)
    {
        $Month =1
        $year +=1
    }
    else
    {
        $Month +=1
    }
}


    switch([DayToReturnInMonth]::$Day)
    {
        FirstMondayOfTheMonth
        {
            $WeekDay = 1
            $WeekNumber = 1
        }
        FirstThursdayOfTheMonth
        {
            $WeekDay = 4
            $WeekNumber = 1
        }
        SecondMondayOfTheMonth
        {
            $WeekDay = 1
            $WeekNumber = 2
        }
        SecondTuesdayOfTheMonth
        {
            $WeekDay = 2
            $WeekNumber = 2
        }
        SecondWednesdayOfTheMonth
        {
            $WeekDay = 3
            $WeekNumber = 2
        }
        SecondThursdayOfTheMonth
        {
            $WeekDay = 4
            $WeekNumber = 2
        }
        ForthMondayOfTheMonth
        {            
            $WeekDay = 1
            $WeekNumber = 4
        }
        ThirdSundayOfTheMonth
        {            
            $WeekDay = 0
            $WeekNumber = 3
        }
        ForthSundayOfTheMonth
        {            
            $WeekDay = 0
            $WeekNumber = 4
        }
        ThirdMondayOfTheMonth
        {
            $WeekDay = 1
            $WeekNumber = 3
        }
        ThirdTuesdayOfTheMonth
        {
            $WeekDay = 2
            $WeekNumber = 3
        }
        ThirdWednesdayOfTheMonth
        {
            $WeekDay = 3
            $WeekNumber = 3
        }
        ThirdThursdayOfTheMonth
        {
            $WeekDay = 4
            $WeekNumber = 3
        }
        ThirdFridayOfTheMonth
        {
            $WeekDay = 5
            $WeekNumber = 3
        }
    }


    $x=1
    $dtmDate = Get-Date -Year $year -Month $month -Day 1 -Hour 0 -Minute 0 -Second 0

    while( $x -eq 1)
    {
        $intDayOfWeek = $dtmDate.DayOfWeek
        if ([int]$intDayOfWeek -eq $WeekDay )
        {

            switch($WeekNumber)
            {
                1
                {
                    $dtmDate = $dtmDate
                    
                }
                2
                {
                    $dtmDate = $dtmDate.AddDays(7)
                    
                }
                3
                {
                    $dtmDate = $dtmDate.AddDays(14)
                    
                }
                4
                {
                    $dtmDate = $dtmDate.AddDays(21)
                    
                }
            }
            #write-host "The n-day of the month is "  $dtmDate  -ForegroundColor Green ([DayToReturnInMonth]::$Day)
            $dtmDate

            $x=0
        }
        else
        {
            $dtmDate = $dtmDate.AddDays(1)
        }
        
    }

}


Function New-FolderOnShare
{

PARAM(
        $SUPPackage,
        $SourceServerUNC
)


    Try
    {

        #Create folder on share if missing
        Set-Location c:
        if((Test-Path -Path ($SourceServerUNC+$SUPPackage)) -eq 0 )
        {
            New-Item -Path $SourceServerUNC -Type directory -name $SUPPackage

            $SourceServerUNC = $SourceServerUNC+$SUPPackage
            if((Test-Path -Path ($SourceServerUNC)) -eq 0 )
            {
                break
            }
        }
        else
        {
            $SourceServerUNC = $SourceServerUNC+$SUPPackage
        }
        
    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}


Function Run-ADR
{
PARAM
(
        $SUPPackage,
        $SUPPackageFullPath,
        $Product,
        $Titles,
        [bool]$ProductSearch
)

    Try{
        cd $global:PSDrive

        Write-Host "deleting old ADR"
        if((Get-CMSoftwareUpdateAutoDeploymentRule -Name $global:ADRName) -ne $null )
        {
            Remove-CMSoftwareUpdateAutoDeploymentRule -Name  $global:ADRName -Force
        }

        Write-Host "Creating new ADR with dummy deployment that is not activated"

        if($global:doc.ADRs.Common.TestRunWithArticle -ne 0)
        {
            New-CMSoftwareUpdateAutoDeploymentRule -CollectionName $global:doc.ADRs.Common.CollectionName -DeploymentPackageName $SUPPackage -Name $global:ADRName -AddToExistingSoftwareUpdateGroup $False -EnabledAfterCreate $false -UpdateClassification $global:UpdateClassification -ArticleId $global:doc.ADRs.Common.TestRunWithArticle -RunType DoNotRunThisRuleAutomatically -AvailableImmediately $true -DeadlineImmediately $true -UserNotification HideAll -UseUtc $False -SuppressRestartServer $False -SuppressRestartWorkstation $False -AllowRestart $False -AllowSoftwareInstallationOutsideMaintenanceWindow $False -WriteFilterHandling $true -GenerateSuccessAlert $False -DownloadFromInternet $True -DownloadFromMicrosoftUpdate $False -UseBranchCache $true -AllowUseMeteredNetwork $False -NoInstallOnRemote $False -NoInstallOnUnprotected $False -LanguageSelection $global:doc.ADRs.Common.LanguageSelection -MicrosoftAsVendor $False -GenerateOperationManagerAlert $False -DisableOperationManager $False -SendWakeUpPacket $False -VerboseLevel AllMessages -DeployWithoutLicense $false -Location $SUPPackageFullPath
        }
        elseif($ProductSearch -eq $true)
        {
            New-CMSoftwareUpdateAutoDeploymentRule -CollectionName $global:doc.ADRs.Common.CollectionName -DeploymentPackageName $SUPPackage -Name $global:ADRName -AddToExistingSoftwareUpdateGroup $False -EnabledAfterCreate $false -UpdateClassification $global:UpdateClassification -DateReleasedOrRevised $global:DateReleasedOrRevised -RunType DoNotRunThisRuleAutomatically -AvailableImmediately $true -DeadlineImmediately $true -UserNotification HideAll -UseUtc $False -SuppressRestartServer $False -SuppressRestartWorkstation $False -AllowRestart $False -AllowSoftwareInstallationOutsideMaintenanceWindow $False -WriteFilterHandling $true -GenerateSuccessAlert $False -DownloadFromInternet $True -DownloadFromMicrosoftUpdate $False -UseBranchCache $true -AllowUseMeteredNetwork $False -NoInstallOnRemote $False -NoInstallOnUnprotected $False -LanguageSelection $global:doc.ADRs.Common.LanguageSelection -MicrosoftAsVendor $False -GenerateOperationManagerAlert $False -DisableOperationManager $False -SendWakeUpPacket $False -VerboseLevel AllMessages -DeployWithoutLicense $false -Location $SUPPackageFullPath -Title $Titles -Product $Product
        }
        else
        {
            New-CMSoftwareUpdateAutoDeploymentRule -CollectionName $global:doc.ADRs.Common.CollectionName -DeploymentPackageName $SUPPackage -Name $global:ADRName -AddToExistingSoftwareUpdateGroup $False -EnabledAfterCreate $false -UpdateClassification $global:UpdateClassification -DateReleasedOrRevised $global:DateReleasedOrRevised -RunType DoNotRunThisRuleAutomatically -AvailableImmediately $true -DeadlineImmediately $true -UserNotification HideAll -UseUtc $False -SuppressRestartServer $False -SuppressRestartWorkstation $False -AllowRestart $False -AllowSoftwareInstallationOutsideMaintenanceWindow $False -WriteFilterHandling $true -GenerateSuccessAlert $False -DownloadFromInternet $True -DownloadFromMicrosoftUpdate $False -UseBranchCache $true -AllowUseMeteredNetwork $False -NoInstallOnRemote $False -NoInstallOnUnprotected $False -LanguageSelection $global:doc.ADRs.Common.LanguageSelection -MicrosoftAsVendor $False -GenerateOperationManagerAlert $False -DisableOperationManager $False -SendWakeUpPacket $False -VerboseLevel AllMessages -DeployWithoutLicense $false -Location $SUPPackageFullPath -Title $Titles
        }

        Write-Host "waiting $global:waiting seconds for ADR..."
        Wait-Event -Timeout $global:waiting

        $timestamp = Get-Date
        Write-Host "Invoking ADR at $timestamp"
        Invoke-CMSoftwareUpdateAutoDeploymentRule -Name $global:ADRName
        $timestamp = Get-Date
        Write-Host "ADR invoked at $timestamp"
    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}


Function Rename-SUG
{
param
(
    $retryWaiting,
    $waiting,
    $SUPPackage,
    $DeleteSUGWhenFound
)

        cd $global:PSDrive

        $FoundSUG = $False        
        $SoftwareUpdateGroup = $null


            while ($FoundSUG -eq $False)
            {
                $SUGName = $global:ADRName + " " + (get-date -Format "yyyy-MM-dd") +"*"

                $ADR = Get-CMSoftwareUpdateAutoDeploymentRule -Name $global:ADRName
                if($ADR.LastRunTime -ne $null)
                {#ADR ran
                                        
                    Wait-Event -Timeout 10 #wait 30 seconds to be on the safe side                    

                    $SoftwareUpdateGroup = Get-CMSoftwareUpdateGroup | sort LocalizedDisplayName | where LocalizedDisplayName -like $SUGName

                    if($SoftwareUpdateGroup.count -eq 0)
                    {#no sug exist
                        return $FoundSUG
                    }
                    else
                    {
                        Write-Host "SUG found."
                        $FoundSUG = $true
                    
                        if($DeleteSUGWhenFound -eq $true)
                        {#If SUG exist, delete it, no need to keep it right now since we will deploy all patches in one big SUG
                            Write-Host "Deleting SUG"
                            Remove-CMSoftwareUpdateGroup -Id $SoftwareUpdateGroup.CI_ID -Force:$true
                        }
                        else
                        {
                            Write-Host "Renaming SUG"
                            Set-CMSoftwareUpdateGroup -Name $SoftwareUpdateGroup.LocalizedDisplayName -NewName $SUPPackage
                        }
                    }
                }
                else
                {
                        Write-Host "waiting $waiting seconds for ADR $SUGName"
                        Wait-Event -Timeout $waiting

                        $retryTimes +=1
                        if($retryTimes -ge $retryWaiting)
                        {
                            return $FoundSUG
                        }
                }

            }
    return $FoundSUG
}


Function Create-SoftwareUpdateDeployments
{

PARAM
(
    [Parameter(Mandatory=$true)][DateTime]$FromWhatDateTime,
    [Parameter(Mandatory=$true)][string]$PSDrive,
    [Parameter(Mandatory=$true)][xml]$xmlConfiguration,
    [Parameter(Mandatory=$true)][string]$SoftwareUpdateGroup,
    [Parameter(Mandatory=$true)][string]$ChangeOrderNumber
)

    cd ($PSDrive+":")

    $SoftwareUpdateDeployments = $xmlConfiguration.ADRs.SoftwareUpdateDeployments.SoftwareUpdateDeployment
    foreach($SoftwareUpdateDeployment in $SoftwareUpdateDeployments)
    {
        Create-SoftwareUpdateDeployment -FromWhatDateTime $FromWhatDateTime -PSDrive $PSDrive -SoftwareUpdateDeployment $SoftwareUpdateDeployment -SoftwareUpdateGroup $SoftwareUpdateGroup -xmlConfiguration $xmlConfiguration -ChangeOrderNumber $ChangeOrderNumber
    }
        
}


Function Create-SoftwareUpdateDeployment
{

PARAM
(
    [Parameter(Mandatory=$true)][DateTime]$FromWhatDateTime,
    [Parameter(Mandatory=$true)]$PSDrive,
    [Parameter(Mandatory=$true)][xml]$xmlConfiguration,
    [Parameter(Mandatory=$true)]$SoftwareUpdateDeployment,
    [Parameter(Mandatory=$true)]$SoftwareUpdateGroup,
    [Parameter(Mandatory=$true)]$ChangeOrderNumber
)

    [int]$month
    [int]$year

    cd ($PSDrive+":")

    Try{

            Write-Host $SoftwareUpdateDeployment.Name
            Write-Host "waiting $global:waiting seconds"
            Wait-Event -Timeout $global:waiting

            #DeployRightAway
            if( ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.DeployRightAway)) -eq $true)
            {#Citrix Pilot 1, Pilot I Servers + Workstations, Pilot II Servers
            
                if(([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -eq [Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::Available)
                {
                    $DeploymentAvailableDay = $FromWhatDateTime #Get-Date
                    $DeploymentAvailableTime = $FromWhatDateTime #Get-Date
                    Start-CMSoftwareUpdateDeployment -SoftwareUpdateGroupName $SoftwareUpdateGroup -CollectionName $SoftwareUpdateDeployment.CollectionName -DeploymentName ("$SoftwareUpdateGroup - " + $SoftwareUpdateDeployment.Name) -DeploymentType ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -VerbosityLevel $SoftwareUpdateDeployment.VerbosityLevel -UserNotification ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.UserNotificationType]::($SoftwareUpdateDeployment.UserNotification)) -SoftwareInstallation $false -AllowRestart ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.AllowRestart)) -RestartServer ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartServer)) -RestartWorkstation ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartWorkstation)) -DownloadFromMicrosoftUpdate $false -AllowUseMeteredNetwork $false -DeploymentAvailableDay $DeploymentAvailableDay -DeploymentAvailableTime $DeploymentAvailableTime 
                }
                else
                {
                    $DeploymentAvailableDay = $FromWhatDateTime #Get-Date
                    $DeploymentAvailableTime = $FromWhatDateTime #Get-Date
                    $DeploymentExpireDay = $FromWhatDateTime #Get-Date
                    $DeploymentExpireTime = $FromWhatDateTime #Get-Date

                    Start-CMSoftwareUpdateDeployment -SoftwareUpdateGroupName $SoftwareUpdateGroup -CollectionName $SoftwareUpdateDeployment.CollectionName -DeploymentName ("$SoftwareUpdateGroup - " + $SoftwareUpdateDeployment.Name) -DeploymentType ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -VerbosityLevel $SoftwareUpdateDeployment.VerbosityLevel -UserNotification ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.UserNotificationType]::($SoftwareUpdateDeployment.UserNotification)) -SoftwareInstallation $false -AllowRestart ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.AllowRestart)) -RestartServer ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartServer)) -RestartWorkstation ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartWorkstation)) -DownloadFromMicrosoftUpdate $false -AllowUseMeteredNetwork $false -DeploymentAvailableDay $DeploymentAvailableDay -DeploymentAvailableTime $DeploymentAvailableTime -DeploymentExpireDay $DeploymentExpireDay -DeploymentExpireTime $DeploymentExpireTime

                }
            }
            else
            {
                if(([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -eq [Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::Available)
                {
                    if(  ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.DeployToDayInMonth)) -eq $true )
                    {#Citrix Pilot 3, Citrix Pilot 4, Manual, Citrix Production 1, Citrix Production 2

                        [int]$month = ($FromWhatDateTime).Month #(Get-Date).Month
                        [int]$year = ($FromWhatDateTime).Year #(Get-Date).Year

                         #deploy to DeployToDayInMonth for Citrix (Availible type with no end date)
                        $DeploymentAvailableDay = Get-WeekDayInMonth -Month $month -year $year -Day ([DayToReturnInMonth]::($SoftwareUpdateDeployment.DeploymentAvailableDay)) -Plus1Month ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.Plus1Month))
                        $DeploymentAvailableTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentAvailableTime)
                    }
                    else
                    {#Citrix Pilot 2
                        $DeploymentAvailableDay = ($FromWhatDateTime).AddDays($SoftwareUpdateDeployment.DeploymentAvailableDay) # (Get-Date).AddDays($SoftwareUpdateDeployment.DeploymentAvailableDay)
                        $DeploymentAvailableTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentAvailableTime)
                    }

                    Start-CMSoftwareUpdateDeployment -SoftwareUpdateGroupName $SoftwareUpdateGroup -CollectionName $SoftwareUpdateDeployment.CollectionName -DeploymentName ("$SoftwareUpdateGroup - " + $SoftwareUpdateDeployment.Name) -DeploymentType ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -VerbosityLevel $SoftwareUpdateDeployment.VerbosityLevel -UserNotification ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.UserNotificationType]::($SoftwareUpdateDeployment.UserNotification)) -SoftwareInstallation $false -AllowRestart ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.AllowRestart)) -RestartServer ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartServer)) -RestartWorkstation ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartWorkstation)) -DownloadFromMicrosoftUpdate $false -AllowUseMeteredNetwork $false -DeploymentAvailableDay $DeploymentAvailableDay -DeploymentAvailableTime $DeploymentAvailableTime
                }
                elseif (([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -eq [Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::Required)
                {
                    if(  ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.DeployToDayInMonth)) -eq $true )
                    {#Pilot III Workstations, Automatic, Automatic - Suppress Reboot
                        [int]$month = ($FromWhatDateTime).Month #(Get-Date).Month
                        [int]$year = ($FromWhatDateTime).Year #(Get-Date).Year

                         #deploy to DeployToDayInMonth for Citrix (Availible type with no end date)
                        $DeploymentAvailableDay = Get-WeekDayInMonth -Month $month -year $year -Day ([DayToReturnInMonth]::($SoftwareUpdateDeployment.DeploymentAvailableDay)) -Plus1Month ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.Plus1Month))
                        $DeploymentAvailableTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentAvailableTime)
                        $DeploymentExpireDay = Get-WeekDayInMonth -Month $month -year $year -Day ([DayToReturnInMonth]::($SoftwareUpdateDeployment.DeploymentExpireDay)) -Plus1Month ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.Plus1Month))
                        $DeploymentExpireTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentExpireTime)
                    }
                    else
                    {#Pilot II Workstations + FAKE
                        $DeploymentAvailableDay = ($FromWhatDateTime).AddDays($SoftwareUpdateDeployment.DeploymentAvailableDay) 
                        $DeploymentAvailableTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentAvailableTime)
                        $DeploymentExpireDay = ($FromWhatDateTime).AddDays($SoftwareUpdateDeployment.DeploymentExpireDay) 
                        $DeploymentExpireTime = ([DateTime]$SoftwareUpdateDeployment.DeploymentExpireTime)

                    }
                    Start-CMSoftwareUpdateDeployment -SoftwareUpdateGroupName $SoftwareUpdateGroup -CollectionName $SoftwareUpdateDeployment.CollectionName -DeploymentName ("$SoftwareUpdateGroup - " + $SoftwareUpdateDeployment.Name) -DeploymentType ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.DeploymentType]::($SoftwareUpdateDeployment.DeploymentType)) -VerbosityLevel $SoftwareUpdateDeployment.VerbosityLevel -UserNotification ([Microsoft.ConfigurationManagement.Cmdlets.Sum.Commands.UserNotificationType]::($SoftwareUpdateDeployment.UserNotification)) -SoftwareInstallation $false -AllowRestart ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.AllowRestart)) -RestartServer ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartServer)) -RestartWorkstation ([System.Convert]::ToBoolean($SoftwareUpdateDeployment.RestartWorkstation)) -DownloadFromMicrosoftUpdate $false -AllowUseMeteredNetwork $false -DeploymentAvailableDay $DeploymentAvailableDay -DeploymentAvailableTime $DeploymentAvailableTime -DeploymentExpireDay $DeploymentExpireDay -DeploymentExpireTime $DeploymentExpireTime

                }
            }
            #"EnabledAfterCreate" parameter does not exist, all SUD enabled right away.

    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
 
       
    $CMDeployments = Get-CMDeployment -CollectionName $SoftwareUpdateDeployment.CollectionName | where SoftwareName -EQ $SoftwareUpdateGroup
            
    foreach($CMDeployment in $CMDeployments)
    {
        New-ScheduledSQLReportSubscriptions -xmlDocumentWithConfiguration $xmlConfiguration -SoftwareUpdateDeployment $SoftwareUpdateDeployment -ChangeOrderNumber $ChangeOrderNumber -DeploymentId $CMDeployment.DeploymentId -CMDeployment $CMDeployment
    }
        
        
}


Function Distribute-To-Primary-DP
{
PARAM
(
    [Parameter(Mandatory=$true)]$SiteServer
)

    

    Try{
        #Distribute to Primary DP
        Start-CMContentDistribution -DeploymentPackageName $global:SUPPackage -DistributionPointName $global:PrimaryDP
        
        $global:AllSUG = Get-CMDeploymentPackage -DistributionPointName $global:doc.ADRs.Common.PrimaryDP -DeploymentPackageName $global:SUPPackage
        
        
    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
    }
}


Function Prestage-SoftwareUpdate-Package
{
    cd $global:PSDrive
    $global:PackageID = ((Get-CMSoftwareUpdateDeploymentPackage -Name $Global:SUPPackage) | select PackageID).PackageID
    
    Try{
    #Prestage the $global:SUPPackage
        Write-Host "exporting SUP with id $global:PackageID"
        Publish-CMPrestageContent -DeploymentPackageName  $global:SUPPackage -FileName "$global:PreStagePackage\SoftwareUpdatePackage-$global:PackageID.pkgx" -DistributionPointName $global:PrimaryDP -ErrorAction Continue
        if ((Test-Path -Path "$global:PreStagePackage\SoftwareUpdatePackage-$global:PackageID.pkgx") -eq 0)
        {
            Write-Host "No export of SUP with id $global:PackageID was done!!!"
            $false
        }
        else
        {
            Remove-PSDrive -Name PrestageDrive -PSProvider FileSystem -ErrorAction Continue
            New-PSDrive -Name PrestageDrive -PSProvider FileSystem -Root $global:PrestageDrive
            Copy-Item -Path "$global:PreStagePackage\SoftwareUpdatePackage-$global:PackageID.pkgx" -Destination PrestageDrive:\
            Remove-PSDrive -Name PrestageDrive -PSProvider FileSystem -ErrorAction Continue
            $true
        }
        
    }
    Catch{
        $E = $_.Exception.GetBaseException()
        $E.ErrorInformation.Description
        $false
    }
}

Function Distribute-To-DPGroup
{
param
(
    $DPs,
    $SUPPackage
)    

        foreach($dp in $DPs)
        {
            #Distribute to DP Group
            Write-Host "Distribute to DP Group $dp"
            Start-CMContentDistribution -DeploymentPackageName $SUPPackage -DistributionPointGroupName $dp
        }

        
        
}


Function Create-All-SoftwareUpdateDeployment-ForSpecialDate    
{
    PARAM
    (
        [Parameter(Mandatory=$true)][DateTime]$FromWhatDateTime,
        #SUPPackage, also know as SUG
        $SUG
    )

    $global:SUPPackage = $SUG
    Create-All-SoftwareUpdateDeployment ($FromWhatDateTime)

}




Function Dowload-Cab-Catalogs
{

    #download file
    $storageDir = $global:doc.ADRs.Common.XMLDownloadLocation

    $scupCatalogs = $global:doc.ADRs.SCUPCatalogs.SCUPCatalog
    foreach($scupCatalog in $scupCatalogs)
    {
        $webclient = New-Object System.Net.WebClient    
        $url = $scupCatalog.CabUrl
        $file = $storageDir+"\"+$scupCatalog.CabFile

        $webclient.DownloadFile($url,$file)

        if((Test-Path -Path ($storageDir+"\"+$scupCatalog.StorageDir)) -eq 0 )
        {
            New-Item $storageDir -Type directory -name $scupCatalog.StorageDir
        }

        expand $file /f:$scupCatalog.XmlFile ($storageDir+"\"+$scupCatalog.StorageDir+"\"+$scupCatalog.XmlFile)
    }

}

Function Dowload-MSP-files
{
    $storageDir = $global:doc.ADRs.Common.XMLDownloadLocation
    $scupCatalogs = $global:doc.ADRs.SCUPCatalogs.SCUPCatalog
    foreach($scupCatalog in $scupCatalogs)
    {
        $webclient = New-Object System.Net.WebClient
        [xml]$SCUPXml = (New-Object System.Net.WebClient).DownloadString($storageDir+"\"+$scupCatalog.StorageDir+"\"+$scupCatalog.XmlFile)
        $ScupXmlCount = $SCUPXml.SystemsManagementCatalog.SoftwareDistributionPackage.Count -1
        $url = $SCUPXml.SystemsManagementCatalog.SoftwareDistributionPackage[$scupxmlcount].InstallableItem.OriginFile.OriginUri
        #Todo find all last dates and download them all
        #([datetime]($SCUPXml.SystemsManagementCatalog.SoftwareDistributionPackage[$scupxmlcount].Properties.CreationDate)).DateTime
        
        $file = "$storageDir\" + $SCUPXml.SystemsManagementCatalog.SoftwareDistributionPackage[$scupxmlcount].InstallableItem.OriginFile.FileName
        $webclient.DownloadFile($url,$file)

    }
}

Function Adobe-Updates
{

    Dowload-Cab-Catalogs
    Dowload-MSP-files

}

Function Adobe-Test
{

    #download file
    $storageDir = "C:\Powershell\Download"

    $webclient = New-Object System.Net.WebClient
    $url = "http://armmf.adobe.com/arm-manifests/win/SCUP/Reader11_Catalog.cab"
    $file = "$storageDir\Reader11_Catalog.cab"
    $webclient.DownloadFile($url,$file)
    New-Item $storageDir -Type directory -name "AdobeReader"
    expand $file /f:Reader11_Catalog.xml "$storageDir\AdobeReader\Reader11_Catalog.xml"

    [xml]$global:doc = (New-Object System.Net.WebClient).DownloadString("$storageDir\AdobeReader\reader11_catalog.xml")


    $url = $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].InstallableItem.OriginFile.OriginUri
    $file = "$storageDir\" + $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].InstallableItem.OriginFile.FileName
    $webclient.DownloadFile($url,$file)

    $pkgPath = $file

    # Load WSUS namespace and create SoftwareDistributionPackage object 
    [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null 
    $sdp = new-object('microsoft.updateservices.administration.SoftwareDistributionPackage') 
    $sdp.PopulatePackageFromWindowsInstallerPatch($pkgPath) 

    #$packageType = [Microsoft.UpdateServices.Administration.PackageType]::($Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.UpdateType)

    # Add properties 
    $sdp.Title = $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].LocalizedProperties.Title
    $sdp.Description = $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].LocalizedProperties.Description
    $sdp.VendorName = $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.VendorName
    $sdp.PackageType =  "1"                 # Update $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.UpdateType
    $sdp.PackageUpdateClassification = "3"         # Security Updates $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].UpdateSpecificData.UpdateClassification
    $sdp.SecurityRating =  "1"             # Critical $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].UpdateSpecificData.MsrcSeverity
    $sdp.ProductNames.Add($Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.ProductName) | out-null 
    $sdp.AdditionalInformationUrls.Add($Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.MoreInfoUrl)
    $sdp.AdditionalInformationUrls.Add($Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].Properties.SupportUrl)

    # Get hash value of MSP file 
    $pkg = (new-object("System.IO.FileInfo")($pkgPath)) 
    $provider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider 
    $digest64 = [System.Convert]::ToBase64String($provider.ComputeHash($pkg.OpenRead())) 
    $instream.Close | out-null 

    # Define file properties of MSP 
    $FileItem = new-object('microsoft.updateservices.administration.FileForInstallableItem') 
    $FileItem.FileName = $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].InstallableItem.OriginFile.FileName
    $FileItem.OriginUri = "file://sccm12server/download$/" + $Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].InstallableItem.OriginFile.FileName
    $FileItem.Digest = $digest64 
    $FileItem.Modified = $pkg.LastWriteTimeUtc 
    $FileItem.Size = $pkg.Length 
    
    $sdp.InstallableItems[0].OriginalSourceFile = $FileItem 

    # create update catalog file 
    $xmlFileName = ($Global:doc.SystemsManagementCatalog.SoftwareDistributionPackage[12].InstallableItem.OriginFile.FileName) -replace ".msp",".xml"
    $sdpFilePath = $storageDir +"\"+ $xmlFileName
    $sdp.Save($sdpFilePath)     

    $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer("sccm12server.your.fqdn",$false,"8530")
    $publisher = $wsus.GetPublisher($sdpFilePath) 
    $dir = $pkg.Directory 
    $publisher.PublishPackage($dir, $null) 

}